#if !defined(PROGRAM_OPTIONS_HELPER_H)
#define PROGRAM_OPTIONS_HELPER_H

#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <boost/program_options.hpp>
#include <boost/any.hpp>

namespace program_options_helper
{
class t_std_program_args_with_errors
{
public:
	// for initializing this struct
	t_std_program_args_with_errors();
	//
	// for error / progress messages etc.
	//
public:
	// N.B.				CERR		defaults to cerr
	std::ostream&		VERBOSE(unsigned i = 1) {return (verbose >= i) ? std::cerr : strm_ignore;}
	std::ostream&		VERBOSE_ONLY(unsigned i = 1) {return (verbose == i) ? std::cerr : strm_ignore;}
	std::ostream&		CERR(){if (logging_errors()) return f_err_log; return std::cerr;}
	std::ostream&		IGNORE(){return strm_ignore;}
	unsigned			is_verbose() const		{return verbose;}
	bool 				logging_errors() const	{return log_to_file;}
	void				open_err_log();
	std::string			err_log_name;
	boost::
	program_options_lg::
	options_description optional_args;
	void				add_std_options();
	void				use_results_summary();
	//
	//	open file 
	// 		1. if not already open
	// 		2. if there is a name
	// 		3. if it is specified to be used
	// 	otherwise just return file
	//
	std::ofstream&		SUMMARY()
	{
		if (!f_results_summary.is_open() &&
			!results_summary.empty() && 
			results_summary_in_use)
		{
			f_results_summary.open(results_summary.c_str());
			if (!f_results_summary)
			{
				results_summary_in_use = false;
				throw std::runtime_error("Could not open " + results_summary);
			}
		}
		return f_results_summary;
	}
private:
	std::ofstream		f_results_summary;
	bool				results_summary_in_use;
	std::string			results_summary;
	unsigned			verbose;
	bool				log_to_file;
	std::ofstream		f_err_log;
	std::ostringstream 	strm_ignore;
};



void print_usage(std::ostream& os, const std::string& exe_name, const std::string& exe_param_line,
				boost::program_options_lg::options_description& options, std::string exe_description,
				unsigned line_len);
void print_version(std::ostream& os, const std::string& exe_name, const std::string& version,
				unsigned line_len = 80);
void check_required_options(const boost::program_options_lg::variables_map& vm,
							const boost::program_options_lg::options_description& desc);

void parse_indices(const std::string& search_str, std::vector<unsigned>& indices);

struct t_option_level {
public:
	unsigned n;
	t_option_level():n(0){}
	t_option_level& inc(){++n; return *this;}
};

inline t_option_level* option_level(unsigned* u)
{return reinterpret_cast<t_option_level*>(u);}


}

// allow multiple bool options?
void validate(boost::any& v, const std::vector<std::string>& values,
				program_options_helper::t_option_level* target_type, int);


// validate unsigned
void validate(boost::any& v, 
              const std::vector<std::string>& values,
              unsigned* target_type, int);

#endif
